#include "Indexador.h"
#include "Merger.h"

Indexador::Indexador(string dataPath, JNIEnv* jniEnv, jobject jniObj, bool mode) {

	crearIndexador(dataPath, 0, jniEnv, jniObj, mode);
}

Indexador::Indexador(string dataPath, int numRegsIndiceParcial, JNIEnv* jniEnv, jobject jniObj) {
	crearIndexador(dataPath, numRegsIndiceParcial, jniEnv, jniObj, false);
}

void Indexador::crearIndexador (string dataPath, int numRegsIndiceParcial, JNIEnv* jniEnv, jobject jniObj, bool mode) {

	Entorno::Instance().setDataPath(dataPath);
	Entorno::Instance().setJniEnv(jniEnv);
	Entorno::Instance().setJniObj(jniObj);

	this->almacenamientoIndice = NULL;
	this->almacenamientoDocs =NULL;
	this->documentoActual = 0;
	this->lexico = NULL;

	if (mode) this->lexicoDocs = new LexicoDocs(CREAR);

	this->offsetLexicoDocs = 0;
	this->almacenamientoDocFrec = NULL;

	if (numRegsIndiceParcial != 0)
		this->numRegsIndiceParcial = numRegsIndiceParcial;
	else
		this->numRegsIndiceParcial = MAX_REGISTROS;
}

Indexador::~Indexador() {
	delete lexicoDocs;
}

void Indexador::nuevoDocumento(string nombreDoc) {

	insertarRegDocs(offsetLexicoDocs);
	lexicoDocs->escribirNombreDoc(nombreDoc);
	offsetLexicoDocs += nombreDoc.size() + sizeof (short int);
	this->documentoActual++;
}

int Indexador::insertarTermino(char * termino) {

	int posicion_a_insertar;
	RegistroIndiceMemoria * aux_reg = NULL;


	verificarEspacioIndiceParcial();

	//verificamos si el termino esta o no en el indice
	if (!indice.empty()) {
		if(busqueda_indice(termino, &posicion_a_insertar) !=-1){
			// Actualizamos el registro del termino
			//indice[posicion_a_insertar]->incrementarFrecuenciaAbsoluta();
			indice[posicion_a_insertar]->actualizarRegistro(documentoActual);
			//delete [] termino;

		} else {
			//hago el corrimiento para dejar el espacio para el nuevo termino
			if (indice.size()>0) {
				indice.push_back(indice[indice.size()-1]);
				for(int i=indice.size()-1 ; i>posicion_a_insertar ; i--) {
					indice[i] = indice[i-1];
				}
				aux_reg = new RegistroIndiceMemoria(termino,documentoActual);
				indice[posicion_a_insertar] = aux_reg;
			}
		}
	} else {
		aux_reg = new RegistroIndiceMemoria(termino,documentoActual);
		indice.push_back(aux_reg);
	}

	return 1;
}

void Indexador::generarIndice() {

	// Para almacenar el ultimo fragmento del indice que puede no estar lleno
	this->guardarIndiceParcial();
	
	this->guardarIndiceDocumentos();
	
	int * resultado = new int[2];
	
	resultado[0] = AlmacenamientoIndice::getCodigoArchivo();
	resultado[1] = this->documentoActual; 
	
	// mergeamos
	Merger * merger = new Merger(resultado[0], resultado[1], 0);
	merger->merge();
	delete merger;
	delete [] resultado;
}

tListaDocDistancias* Indexador::getDocs(int offset, int frec) {

	tListaDocDistancias* listaDocDistancias;

	almacenamientoDocFrec = new AlmacenamientoDocFrec(ABRIR, 0);
	almacenamientoDocFrec->abrirArchivo();
	listaDocDistancias = almacenamientoDocFrec->leer(offset, frec, true);

	return listaDocDistancias;
}

void Indexador::insertarRegDocs(int offset) {

	RegistroIndiceDocumentos * reg = new RegistroIndiceDocumentos();

	reg->setOffsetLexico(offset);

	indiceDocs.push_back(reg);
}

/* verifica que no se haya llegado al maximo de registros por indice parcial.
 * si se llegó se lo manda a disco y se lo resetea */
void Indexador::verificarEspacioIndiceParcial() {
	
	if (indice.size() >= this->numRegsIndiceParcial) {
		// Escribir header con cantidad de registros
		// Guardar archivo parcial en disco
		//
		guardarIndiceParcial();
		indice.clear();
	}
}

/* realiza busqueda binaria en el indice en memoria */
int Indexador::busqueda_indice(char * termino, int *posicion_a_insertar) {

	RegistroIndiceMemoria * regIndiceMem = NULL;
	int primero = 0;
	int ultimo = this->indice.size()-1;

	while (primero <= ultimo) {
		int mitad = (primero + ultimo) / 2;  // se calcula la mitad
		regIndiceMem = this->indice[mitad];
		char * clave = regIndiceMem->getTermino();
		
		if (strcmp (clave, termino) < 0) {
			primero = mitad + 1;  // repite busqueda en 2da mitad
		}
		else if (strcmp (clave, termino) > 0) {
			ultimo = mitad - 1; // repite busqueda en 1ra mitad
		}
		else {
			*posicion_a_insertar = mitad;
			return mitad; // se encontr??? el registro
		}
	}
	*posicion_a_insertar = primero;
	return -1; // no se encontr??? el registro
}

/* manda a disco el indice parcial que se creo en memoria */
void Indexador::guardarIndiceParcial() {

	unsigned int i;
	int offsetLexicoActual = 0, offsetDocsActual = 0;
	RegistroIndice * reg = NULL;

	lexico = new Lexico(CREAR, 0, true);
	lexico->abrirArchivo();
	almacenamientoIndice = new AlmacenamientoIndice(CREAR, 0, true);
	almacenamientoIndice->abrirArchivo();
	almacenamientoDocFrec = new AlmacenamientoDocFrec(CREAR, 0, true);
	almacenamientoDocFrec->abrirArchivo();

	for (i = 0; i < indice.size(); i++) {
		
		// escribimos el termino en el lexico
		offsetLexicoActual = lexico->obtenerOffsetActual();
		lexico->escribirTermino(indice[i]->getTermino());
		
		offsetDocsActual = almacenamientoDocFrec->escribir(indice[i]->getListaDocumentosDistancias(), false);

		// inserto o actualizo una entrada en el arbol
		Entorno::Instance().updateTree(indice[i]->getTermino(), offsetDocsActual, indice[i]->getFrecuenciaAbsoluta());

		indice[i]->setFrecuenciaAbsoluta(indice[i]->getListaDocumentosDistancias()->size());
		reg = new RegistroIndice(offsetLexicoActual, indice[i]->getFrecuenciaAbsoluta(), offsetDocsActual);
		almacenamientoIndice->escribirRegistroIndice(reg);
		//escribir termino en archivo lexico
		offsetLexicoActual += strlen (indice[i]->getTermino()) + 1;
		//escribir registro de archivo doc-frec
		offsetDocsActual = 0;	// Falta calcular valor
		
		delete indice[i];
		
		delete (reg);
	}

	delete almacenamientoIndice;
	delete lexico;
	delete almacenamientoDocFrec;
}

/* manda a disco el indice de los documentos */
void Indexador::guardarIndiceDocumentos() {

	unsigned int i;
	AlmacenamientoDocs * almacenamientoDocs = new AlmacenamientoDocs(CREAR);

	for (i = 0; i < indiceDocs.size(); i++) {

		almacenamientoDocs->escribirRegistroIndiceDocs(indiceDocs[i]);
		delete indiceDocs[i];
	}

	delete almacenamientoDocs;
}
